import * as Cesium from 'cesium';
import { PixelFormatGaussianSplatting } from './fixPixelFormat';
//@ts-ignore
const { defaultValue, Sampler, defined, DeveloperError, ContextLimits, PixelDatatype, createGuid, Cartesian2, MipmapHint,
    Math: CesiumMath, TextureMinificationFilter, TextureMagnificationFilter
} = Cesium;
export class TextureGaussianSplatting extends Cesium.Texture {
    constructor(options: any) {
        super({
            ...options,
            pixelFormat: PixelFormatGaussianSplatting.RGBA
        });
        //@ts-ignore
        options = defaultValue(options, defaultValue.EMPTY_OBJECT);

        //>>includeStart('debug', pragmas.debug);
        Cesium.Check.defined("options.context", options.context);
        //>>includeEnd('debug');

        const {
            context,
            source,
            pixelFormat = PixelFormatGaussianSplatting.RGBA,
            pixelDatatype = PixelFormatGaussianSplatting.UNSIGNED_BYTE,
            flipY = true,
            skipColorSpaceConversion = false,
            sampler = new Sampler(),
        } = options;

        let { width, height } = options;
        if (defined(source)) {
            // Make sure we are using the element's intrinsic width and height where available
            if (!defined(width)) {
                width = source.videoWidth ?? source.naturalWidth ?? source.width;
            }
            if (!defined(height)) {
                height = source.videoHeight ?? source.naturalHeight ?? source.height;
            }
        }

        // Use premultiplied alpha for opaque textures should perform better on Chrome:
        // http://media.tojicode.com/webglCamp4/#20
        const preMultiplyAlpha =
            options.preMultiplyAlpha ||
            pixelFormat === PixelFormatGaussianSplatting.RGB ||
            pixelFormat === PixelFormatGaussianSplatting.LUMINANCE;

        const internalFormat = PixelFormatGaussianSplatting.toInternalFormat(
            pixelFormat,
            pixelDatatype,
            context,
        );

        const isCompressed = PixelFormatGaussianSplatting.isCompressedFormat(internalFormat);

        //>>includeStart('debug', pragmas.debug);
        if (!defined(width) || !defined(height)) {
            throw new DeveloperError(
                "options requires a source field to create an initialized texture or width and height fields to create a blank texture.",
            );
        }

        Cesium.Check.typeOf.number.greaterThan("width", width, 0);

        if (width > ContextLimits.maximumTextureSize) {
            throw new DeveloperError(
                `Width must be less than or equal to the maximum texture size (${ContextLimits.maximumTextureSize}).  Check maximumTextureSize.`,
            );
        }

        Cesium.Check.typeOf.number.greaterThan("height", height, 0);

        if (height > ContextLimits.maximumTextureSize) {
            throw new DeveloperError(
                `Height must be less than or equal to the maximum texture size (${ContextLimits.maximumTextureSize}).  Check maximumTextureSize.`,
            );
        }

        if (!PixelFormatGaussianSplatting.validate(pixelFormat)) {
            throw new DeveloperError("Invalid options.pixelFormat.");
        }
        //@ts-ignore
        if (!isCompressed && !PixelDatatype.validate(pixelDatatype)) {
            throw new DeveloperError("Invalid options.pixelDatatype.");
        }

        if (
            pixelFormat === PixelFormatGaussianSplatting.DEPTH_COMPONENT &&
            pixelDatatype !== PixelDatatype.UNSIGNED_SHORT &&
            pixelDatatype !== PixelDatatype.UNSIGNED_INT
        ) {
            throw new DeveloperError(
                "When options.pixelFormat is DEPTH_COMPONENT, options.pixelDatatype must be UNSIGNED_SHORT or UNSIGNED_INT.",
            );
        }

        if (
            pixelFormat === PixelFormatGaussianSplatting.DEPTH_STENCIL &&
            pixelDatatype !== PixelDatatype.UNSIGNED_INT_24_8
        ) {
            throw new DeveloperError(
                "When options.pixelFormat is DEPTH_STENCIL, options.pixelDatatype must be UNSIGNED_INT_24_8.",
            );
        }

        if (pixelDatatype === PixelDatatype.FLOAT && !context.floatingPointTexture) {
            throw new DeveloperError(
                "When options.pixelDatatype is FLOAT, this WebGL implementation must support the OES_texture_float extension.  Check context.floatingPointTexture.",
            );
        }

        if (
            pixelDatatype === PixelDatatype.HALF_FLOAT &&
            !context.halfFloatingPointTexture
        ) {
            throw new DeveloperError(
                "When options.pixelDatatype is HALF_FLOAT, this WebGL implementation must support the OES_texture_half_float extension. Check context.halfFloatingPointTexture.",
            );
        }

        if (PixelFormatGaussianSplatting.isDepthFormat(pixelFormat)) {
            if (defined(source)) {
                throw new DeveloperError(
                    "When options.pixelFormat is DEPTH_COMPONENT or DEPTH_STENCIL, source cannot be provided.",
                );
            }

            if (!context.depthTexture) {
                throw new DeveloperError(
                    "When options.pixelFormat is DEPTH_COMPONENT or DEPTH_STENCIL, this WebGL implementation must support WEBGL_depth_texture.  Check context.depthTexture.",
                );
            }
        }

        if (isCompressed) {
            if (!defined(source) || !defined(source.arrayBufferView)) {
                throw new DeveloperError(
                    "When options.pixelFormat is compressed, options.source.arrayBufferView must be defined.",
                );
            }

            if (PixelFormatGaussianSplatting.isDXTFormat(internalFormat) && !context.s3tc) {
                throw new DeveloperError(
                    "When options.pixelFormat is S3TC compressed, this WebGL implementation must support the WEBGL_compressed_texture_s3tc extension. Check context.s3tc.",
                );
            } else if (PixelFormatGaussianSplatting.isPVRTCFormat(internalFormat) && !context.pvrtc) {
                throw new DeveloperError(
                    "When options.pixelFormat is PVRTC compressed, this WebGL implementation must support the WEBGL_compressed_texture_pvrtc extension. Check context.pvrtc.",
                );
            } else if (PixelFormatGaussianSplatting.isASTCFormat(internalFormat) && !context.astc) {
                throw new DeveloperError(
                    "When options.pixelFormat is ASTC compressed, this WebGL implementation must support the WEBGL_compressed_texture_astc extension. Check context.astc.",
                );
            } else if (PixelFormatGaussianSplatting.isETC2Format(internalFormat) && !context.etc) {
                throw new DeveloperError(
                    "When options.pixelFormat is ETC2 compressed, this WebGL implementation must support the WEBGL_compressed_texture_etc extension. Check context.etc.",
                );
            } else if (PixelFormatGaussianSplatting.isETC1Format(internalFormat) && !context.etc1) {
                throw new DeveloperError(
                    "When options.pixelFormat is ETC1 compressed, this WebGL implementation must support the WEBGL_compressed_texture_etc1 extension. Check context.etc1.",
                );
            } else if (PixelFormatGaussianSplatting.isBC7Format(internalFormat) && !context.bc7) {
                throw new DeveloperError(
                    "When options.pixelFormat is BC7 compressed, this WebGL implementation must support the EXT_texture_compression_bptc extension. Check context.bc7.",
                );
            }

            if (
                PixelFormatGaussianSplatting.compressedTextureSizeInBytes(
                    internalFormat,
                    width,
                    height,
                ) !== source.arrayBufferView.byteLength
            ) {
                throw new DeveloperError(
                    "The byte length of the array buffer is invalid for the compressed texture with the given width and height.",
                );
            }
        }
        //>>includeEnd('debug');

        const gl = context._gl;

        const sizeInBytes = isCompressed
            ? PixelFormatGaussianSplatting.compressedTextureSizeInBytes(pixelFormat, width, height)
            : PixelFormatGaussianSplatting.textureSizeInBytes(pixelFormat, pixelDatatype, width, height);
        //@ts-ignore
        this._id = options.id ?? createGuid();
        //@ts-ignore
        this._context = context;
        //@ts-ignore
        this._textureFilterAnisotropic = context._textureFilterAnisotropic;
        //@ts-ignore
        this._textureTarget = gl.TEXTURE_2D;
        //@ts-ignore
        this._texture = gl.createTexture();
        //@ts-ignore
        this._internalFormat = internalFormat;
        //@ts-ignore

        this._pixelFormat = pixelFormat;
        //@ts-ignore
        this._pixelDatatype = pixelDatatype;
        //@ts-ignore
        this._width = width;
        //@ts-ignore
        this._height = height;
        //@ts-ignore
        this._dimensions = new Cartesian2(width, height);
        //@ts-ignore
        this._hasMipmap = false;
        //@ts-ignore
        this._sizeInBytes = sizeInBytes;
        //@ts-ignore
        this._preMultiplyAlpha = preMultiplyAlpha;
        //@ts-ignore
        this._flipY = flipY;
        //@ts-ignore
        this._initialized = false;
        //@ts-ignore
        this._sampler = undefined;
        //@ts-ignore
        this._sampler = sampler;
        setupSampler(this, sampler);

        gl.activeTexture(gl.TEXTURE0);
        //@ts-ignore
        gl.bindTexture(this._textureTarget, this._texture);

        if (defined(source)) {
            if (skipColorSpaceConversion) {
                gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
            } else {
                gl.pixelStorei(
                    gl.UNPACK_COLORSPACE_CONVERSION_WEBGL,
                    gl.BROWSER_DEFAULT_WEBGL,
                );
            }
            if (defined(source.arrayBufferView)) {
                const isCompressed = PixelFormatGaussianSplatting.isCompressedFormat(internalFormat);
                if (isCompressed) {
                    loadCompressedBufferSource(this, source);
                } else {
                    loadBufferSource(this, source);
                }
            } else if (defined(source.framebuffer)) {
                loadFramebufferSource(this, source);
            } else {
                loadImageSource(this, source);
            }
            //@ts-ignore
            this._initialized = true;
        } else {
            loadNull(this);
        }
        //@ts-ignore
        gl.bindTexture(this._textureTarget, null);
    }
    fromFramebuffer = (options: any) => {
        //@ts-ignore
        options = defaultValue(options, defaultValue.EMPTY_OBJECT);

        //>>includeStart('debug', pragmas.debug);
        Cesium.Check.defined("options.context", options.context);
        //>>includeEnd('debug');

        const context = options.context;
        const gl = context._gl;
        const {
            pixelFormat = PixelFormatGaussianSplatting.RGB,
            framebufferXOffset = 0,
            framebufferYOffset = 0,
            width = gl.drawingBufferWidth,
            height = gl.drawingBufferHeight,
            framebuffer,
        } = options;

        //>>includeStart('debug', pragmas.debug);
        if (!PixelFormatGaussianSplatting.validate(pixelFormat)) {
            throw new DeveloperError("Invalid pixelFormat.");
        }
        if (
            PixelFormatGaussianSplatting.isDepthFormat(pixelFormat) ||
            PixelFormatGaussianSplatting.isCompressedFormat(pixelFormat)
        ) {
            throw new DeveloperError(
                "pixelFormat cannot be DEPTH_COMPONENT, DEPTH_STENCIL or a compressed format.",
            );
        }
        Cesium.Check.defined("options.context", context);
        Cesium.Check.typeOf.number.greaterThanOrEquals(
            "framebufferXOffset",
            framebufferXOffset,
            0,
        );
        Cesium.Check.typeOf.number.greaterThanOrEquals(
            "framebufferYOffset",
            framebufferYOffset,
            0,
        );
        if (framebufferXOffset + width > gl.drawingBufferWidth) {
            throw new DeveloperError(
                "framebufferXOffset + width must be less than or equal to drawingBufferWidth",
            );
        }
        if (framebufferYOffset + height > gl.drawingBufferHeight) {
            throw new DeveloperError(
                "framebufferYOffset + height must be less than or equal to drawingBufferHeight.",
            );
        }
        //>>includeEnd('debug');

        const texture = new TextureGaussianSplatting({
            context: context,
            width: width,
            height: height,
            pixelFormat: pixelFormat,
            source: {
                framebuffer: defined(framebuffer)
                    ? framebuffer
                    : context.defaultFramebuffer,
                xOffset: framebufferXOffset,
                yOffset: framebufferYOffset,
                width: width,
                height: height,
            },
        });

        return texture;
    };
    override copyFrom = (options: any) => {
        //>>includeStart('debug', pragmas.debug);
        Cesium.Check.defined("options", options);
        //>>includeEnd('debug');

        const {
            xOffset = 0,
            yOffset = 0,
            source,
            skipColorSpaceConversion = false,
        } = options;

        //>>includeStart('debug', pragmas.debug);
        Cesium.Check.defined("options.source", source);
        //@ts-ignore
        if (PixelFormatGaussianSplatting.isDepthFormat(this._pixelFormat)) {
            throw new DeveloperError(
                "Cannot call copyFrom when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL.",
            );
        }
        //@ts-ignore
        if (PixelFormatGaussianSplatting.isCompressedFormat(this._pixelFormat)) {
            throw new DeveloperError(
                "Cannot call copyFrom with a compressed texture pixel format.",
            );
        }
        Cesium.Check.typeOf.number.greaterThanOrEquals("xOffset", xOffset, 0);
        Cesium.Check.typeOf.number.greaterThanOrEquals("yOffset", yOffset, 0);
        Cesium.Check.typeOf.number.lessThanOrEquals(
            "xOffset + options.source.width",
            xOffset + source.width,
            //@ts-ignore
            this._width,
        );
        Cesium.Check.typeOf.number.lessThanOrEquals(
            "yOffset + options.source.height",
            yOffset + source.height,
            //@ts-ignore
            this._height,
        );
        //>>includeEnd('debug');
        //@ts-ignore
        const context = this._context;
        //@ts-ignore
        const gl = context._gl;
        //@ts-ignore
        const target = this._textureTarget;

        gl.activeTexture(gl.TEXTURE0);
        //@ts-ignore
        gl.bindTexture(target, this._texture);

        let { width, height } = source;

        // Make sure we are using the element's intrinsic width and height where available
        if (defined(source.videoWidth) && defined(source.videoHeight)) {
            width = source.videoWidth;
            height = source.videoHeight;
        } else if (defined(source.naturalWidth) && defined(source.naturalHeight)) {
            width = source.naturalWidth;
            height = source.naturalHeight;
        }

        if (skipColorSpaceConversion) {
            gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
        } else {
            gl.pixelStorei(
                gl.UNPACK_COLORSPACE_CONVERSION_WEBGL,
                gl.BROWSER_DEFAULT_WEBGL,
            );
        }

        let uploaded = false;
        //@ts-ignore
        if (!this._initialized) {
            if (
                xOffset === 0 &&
                yOffset === 0 &&
                //@ts-ignore
                width === this._width &&
                //@ts-ignore
                height === this._height
            ) {
                // initialize the entire texture
                if (defined(source.arrayBufferView)) {
                    loadBufferSource(this, source);
                } else {
                    loadImageSource(this, source);
                }
                uploaded = true;
            } else {
                gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
                gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
                loadNull(this);
            }
            //@ts-ignore
            this._initialized = true;
        }

        if (!uploaded) {
            if (defined(source.arrayBufferView)) {
                loadPartialBufferSource(
                    this,
                    source.arrayBufferView,
                    xOffset,
                    yOffset,
                    width,
                    height,
                );
            } else {
                loadPartialImageSource(this, source, xOffset, yOffset);
            }
        }

        gl.bindTexture(target, null);
    };
    copyFromFramebuffer = (
        xOffset: any,
        yOffset: any,
        framebufferXOffset: any,
        framebufferYOffset: any,
        width: any,
        height: any,
    ) => {
        xOffset = defaultValue(xOffset, 0);
        yOffset = defaultValue(yOffset, 0);
        framebufferXOffset = defaultValue(framebufferXOffset, 0);
        framebufferYOffset = defaultValue(framebufferYOffset, 0);
        //@ts-ignore
        width = defaultValue(width, this._width);
        //@ts-ignore
        height = defaultValue(height, this._height);

        //>>includeStart('debug', pragmas.debug);
        //@ts-ignore
        if (PixelFormatGaussianSplatting.isDepthFormat(this._pixelFormat)) {
            throw new DeveloperError(
                "Cannot call copyFromFramebuffer when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL.",
            );
        }
        //@ts-ignore
        if (this._pixelDatatype === PixelDatatype.FLOAT) {
            throw new DeveloperError(
                "Cannot call copyFromFramebuffer when the texture pixel data type is FLOAT.",
            );
        }
        //@ts-ignore
        if (this._pixelDatatype === PixelDatatype.HALF_FLOAT) {
            throw new DeveloperError(
                "Cannot call copyFromFramebuffer when the texture pixel data type is HALF_FLOAT.",
            );
        }
        //@ts-ignore
        if (PixelFormatGaussianSplatting.isCompressedFormat(this._pixelFormat)) {
            throw new DeveloperError(
                "Cannot call copyFrom with a compressed texture pixel format.",
            );
        }

        Cesium.Check.typeOf.number.greaterThanOrEquals("xOffset", xOffset, 0);
        Cesium.Check.typeOf.number.greaterThanOrEquals("yOffset", yOffset, 0);
        Cesium.Check.typeOf.number.greaterThanOrEquals(
            "framebufferXOffset",
            framebufferXOffset,
            0,
        );
        Cesium.Check.typeOf.number.greaterThanOrEquals(
            "framebufferYOffset",
            framebufferYOffset,
            0,
        );
        Cesium.Check.typeOf.number.lessThanOrEquals(
            "xOffset + width",
            xOffset + width,
            //@ts-ignore
            this._width,
        );
        Cesium.Check.typeOf.number.lessThanOrEquals(
            "yOffset + height",
            yOffset + height,
            //@ts-ignore
            this._height,
        );
        //>>includeEnd('debug');
        //@ts-ignore
        const gl = this._context._gl;
        //@ts-ignore
        const target = this._textureTarget;

        gl.activeTexture(gl.TEXTURE0);
        //@ts-ignore
        gl.bindTexture(target, this._texture);
        gl.copyTexSubImage2D(
            target,
            0,
            xOffset,
            yOffset,
            framebufferXOffset,
            framebufferYOffset,
            width,
            height,
        );
        gl.bindTexture(target, null);
        //@ts-ignore
        this._initialized = true;
    };
    override generateMipmap = (hint: any) => {
        hint = defaultValue(hint, MipmapHint.DONT_CARE);

        //>>includeStart('debug', pragmas.debug);
        //@ts-ignore
        if (PixelFormatGaussianSplatting.isDepthFormat(this._pixelFormat)) {
            throw new DeveloperError(
                "Cannot call generateMipmap when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL.",
            );
        }
        //@ts-ignore
        if (PixelFormatGaussianSplatting.isCompressedFormat(this._pixelFormat)) {
            throw new DeveloperError(
                "Cannot call generateMipmap with a compressed pixel format.",
            );
        }
        //@ts-ignore
        if (!this._context.webgl2) {
            //@ts-ignore
            if (this._width > 1 && !CesiumMath.isPowerOfTwo(this._width)) {
                throw new DeveloperError(
                    "width must be a power of two to call generateMipmap() in a WebGL1 context.",
                );
            }
            //@ts-ignore
            if (this._height > 1 && !CesiumMath.isPowerOfTwo(this._height)) {
                throw new DeveloperError(
                    "height must be a power of two to call generateMipmap() in a WebGL1 context.",
                );
            }
        }
        //@ts-ignore
        if (!MipmapHint.validate(hint)) {
            throw new DeveloperError("hint is invalid.");
        }
        //>>includeEnd('debug');
        //@ts-ignore
        this._hasMipmap = true;
        //@ts-ignore
        const gl = this._context._gl;
        //@ts-ignore
        const target = this._textureTarget;

        gl.hint(gl.GENERATE_MIPMAP_HINT, hint);
        gl.activeTexture(gl.TEXTURE0);
        //@ts-ignore
        gl.bindTexture(target, this._texture);
        gl.generateMipmap(target);
        gl.bindTexture(target, null);
    };
}
function setupSampler(texture: any, sampler: any) {
    let { minificationFilter, magnificationFilter } = sampler;

    const mipmap = [
        TextureMinificationFilter.NEAREST_MIPMAP_NEAREST,
        TextureMinificationFilter.NEAREST_MIPMAP_LINEAR,
        TextureMinificationFilter.LINEAR_MIPMAP_NEAREST,
        TextureMinificationFilter.LINEAR_MIPMAP_LINEAR,
    ].includes(minificationFilter);

    const context = texture._context;
    const pixelFormat = texture._pixelFormat;
    const pixelDatatype = texture._pixelDatatype;

    // float textures only support nearest filtering unless the linear extensions are supported
    if (
        (pixelDatatype === PixelDatatype.FLOAT && !context.textureFloatLinear) ||
        (pixelDatatype === PixelDatatype.HALF_FLOAT &&
            !context.textureHalfFloatLinear)
    ) {
        // override the sampler's settings
        minificationFilter = mipmap
            ? TextureMinificationFilter.NEAREST_MIPMAP_NEAREST
            : TextureMinificationFilter.NEAREST;
        magnificationFilter = TextureMagnificationFilter.NEAREST;
    }

    // WebGL 2 depth texture only support nearest filtering. See section 3.8.13 OpenGL ES 3 spec
    if (context.webgl2) {
        if (PixelFormatGaussianSplatting.isDepthFormat(pixelFormat)) {
            minificationFilter = TextureMinificationFilter.NEAREST;
            magnificationFilter = TextureMagnificationFilter.NEAREST;
        }
    }

    const gl = context._gl;
    const target = texture._textureTarget;

    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(target, texture._texture);
    gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, minificationFilter);
    gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, magnificationFilter);
    gl.texParameteri(target, gl.TEXTURE_WRAP_S, sampler.wrapS);
    gl.texParameteri(target, gl.TEXTURE_WRAP_T, sampler.wrapT);
    if (defined(texture._textureFilterAnisotropic)) {
        gl.texParameteri(
            target,
            texture._textureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT,
            sampler.maximumAnisotropy,
        );
    }
    gl.bindTexture(target, null);
}
function loadBufferSource(texture: any, source: any) {
    const context = texture._context;
    const gl = context._gl;
    const textureTarget = texture._textureTarget;
    const internalFormat = texture._internalFormat;

    const { width, height, pixelFormat, pixelDatatype, flipY } = texture;

    const unpackAlignment = PixelFormatGaussianSplatting.alignmentInBytes(
        pixelFormat,
        pixelDatatype,
        width,
    );
    gl.pixelStorei(gl.UNPACK_ALIGNMENT, unpackAlignment);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);

    let { arrayBufferView } = source;
    if (flipY) {
        arrayBufferView = PixelFormatGaussianSplatting.flipY(
            arrayBufferView,
            pixelFormat,
            pixelDatatype,
            width,
            height,
        );
    }
    gl.texImage2D(
        textureTarget,
        0,
        internalFormat,
        width,
        height,
        0,
        pixelFormat,
        //@ts-ignore
        PixelDatatype.toWebGLConstant(pixelDatatype, context),
        arrayBufferView,
    );

    if (defined(source.mipLevels)) {
        let mipWidth = width;
        let mipHeight = height;
        for (let i = 0; i < source.mipLevels.length; ++i) {
            mipWidth = nextMipSize(mipWidth);
            mipHeight = nextMipSize(mipHeight);
            gl.texImage2D(
                textureTarget,
                i + 1,
                internalFormat,
                mipWidth,
                mipHeight,
                0,
                pixelFormat,
                //@ts-ignore
                PixelDatatype.toWebGLConstant(pixelDatatype, context),
                source.mipLevels[i],
            );
        }
    }
}
function loadPartialBufferSource(
    texture: any,
    arrayBufferView: any,
    xOffset: any,
    yOffset: any,
    width: any,
    height: any,
) {
    const context = texture._context;
    const gl = context._gl;

    const { pixelFormat, pixelDatatype } = texture;

    const unpackAlignment = PixelFormatGaussianSplatting.alignmentInBytes(
        pixelFormat,
        pixelDatatype,
        width,
    );
    gl.pixelStorei(gl.UNPACK_ALIGNMENT, unpackAlignment);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);

    if (texture.flipY) {
        arrayBufferView = PixelFormatGaussianSplatting.flipY(
            arrayBufferView,
            pixelFormat,
            pixelDatatype,
            width,
            height,
        );
    }
    gl.texSubImage2D(
        texture._textureTarget,
        0,
        xOffset,
        yOffset,
        width,
        height,
        pixelFormat,
        //@ts-ignore
        PixelDatatype.toWebGLConstant(pixelDatatype, context),
        arrayBufferView,
    );
}
//#region 
function loadImageSource(texture: any, source: any) {
    const context = texture._context;
    const gl = context._gl;

    gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.preMultiplyAlpha);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, texture.flipY);

    gl.texImage2D(
        texture._textureTarget,
        0,
        texture._internalFormat,
        texture.pixelFormat,
        //@ts-ignore
        PixelDatatype.toWebGLConstant(texture.pixelDatatype, context),
        source,
    );
}
function loadNull(texture: any) {
    const context = texture._context;

    context._gl.texImage2D(
        texture._textureTarget,
        0,
        texture._internalFormat,
        texture._width,
        texture._height,
        0,
        texture._pixelFormat,
        //@ts-ignore
        PixelDatatype.toWebGLConstant(texture._pixelDatatype, context),
        null,
    );
}
function loadPartialImageSource(texture: any, source: any, xOffset: any, yOffset: any) {
    const context = texture._context;
    const gl = context._gl;

    gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.preMultiplyAlpha);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, texture.flipY);

    gl.texSubImage2D(
        texture._textureTarget,
        0,
        xOffset,
        yOffset,
        texture.pixelFormat,
        //@ts-ignore
        PixelDatatype.toWebGLConstant(texture.pixelDatatype, context),
        source,
    );
}
function nextMipSize(currentSize: any) {
    const nextSize = Math.floor(currentSize / 2) | 0;
    return Math.max(nextSize, 1);
}
function loadCompressedBufferSource(texture: any, source: any) {
    const context = texture._context;
    const gl = context._gl;
    const textureTarget = texture._textureTarget;
    const internalFormat = texture._internalFormat;

    const { width, height } = texture;

    gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);

    gl.compressedTexImage2D(
        textureTarget,
        0,
        internalFormat,
        width,
        height,
        0,
        source.arrayBufferView,
    );

    if (defined(source.mipLevels)) {
        let mipWidth = width;
        let mipHeight = height;
        for (let i = 0; i < source.mipLevels.length; ++i) {
            mipWidth = nextMipSize(mipWidth);
            mipHeight = nextMipSize(mipHeight);
            gl.compressedTexImage2D(
                textureTarget,
                i + 1,
                internalFormat,
                mipWidth,
                mipHeight,
                0,
                source.mipLevels[i],
            );
        }
    }
}
function loadFramebufferSource(texture: any, source: any) {
    const context = texture._context;
    const gl = context._gl;

    gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);

    if (source.framebuffer !== context.defaultFramebuffer) {
        source.framebuffer._bind();
    }

    gl.copyTexImage2D(
        texture._textureTarget,
        0,
        texture._internalFormat,
        source.xOffset,
        source.yOffset,
        texture.width,
        texture.height,
        0,
    );

    if (source.framebuffer !== context.defaultFramebuffer) {
        source.framebuffer._unBind();
    }
}
//#endregion